﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace ProgramTest
{
    public static class ExpressionTestShow
    {
        public static void Show()
        {
            {
                ////写一个表达式目录树，比较两个int的大小
                //Expression<Func<int,int,bool>> expression=
            }

            {
                Expression<Func<int, int>> expression = x => x + 1;
                //Console.WriteLine(expression.ToString());

                var lambdaExpr = expression as LambdaExpression;
            }
            {
                //创建一个相对复杂的表达式目录树
                Expression call = Expression.Call(
                    null,//调用方法的实例
                    typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }),//定义一个方法，方法是 Console 类 的 静态方法 WriteLine ，传入参数为一个string类型
                    Expression.Constant("Hello")//定义一个常量Hello，它是需要传入的参数，
                );

                LoopExpression loop = Expression.Loop(call);
                BlockExpression block = Expression.Block(loop);
                Expression<Action> action = Expression.Lambda<Action>(block);
                var lambda = action.Compile();
                //lambda.Invoke();
            }
            {
                ParameterExpression number = Expression.Parameter(typeof(int), "number");//定义一个参数 number

                BlockExpression block = Expression.Block(new[] { number },
                        Expression.Assign(number, Expression.Constant(2, typeof(int))),//给参数 number 赋值 一个 int 常量 2
                        Expression.AddAssign(number, Expression.Constant(4, typeof(int))),//给参数 number 加一个 int 常量 4
                        Expression.Divide(number, Expression.Constant(3, typeof(int)))//给参数 number 除以一个 int 常量 3
                    );
                var action = Expression.Lambda<Func<int>>(block);
                var expre = action.Compile();
                //int result = expre.Invoke();
            }
            {
                //创建一个相对复杂的表达式目录树
                LabelTarget labelBreak = Expression.Label();//定义一个标签
                ParameterExpression loopIndex = Expression.Parameter(typeof(int), "index");//定义一个int类型参数表达式
                //10.ToString()
                Expression callTostring = Expression.Call(
                    loopIndex,//调用方法的实例
                    typeof(Int32).GetMethod("ToString", new Type[] { })//定义一个方法，方法是 Int32 类 的 静态方法 ToString ，无传入参数类型
                );

                Expression call = Expression.Call(
                    null,//调用方法的实例
                    typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }),//定义一个方法，方法是 Console 类 的 静态方法 WriteLine ，传入参数为一个string类型
                    callTostring//需要传入的参数，这个地方尝试传入当前编号
                );

                LoopExpression loop = Expression.Loop(
                        Expression.IfThenElse(Expression.LessThanOrEqual(loopIndex, Expression.Constant(10)),// if 的判断逻辑，小于等于10
                        Expression.Block(call, Expression.PostIncrementAssign(loopIndex)),// 判断逻辑通过的代码
                        Expression.Break(labelBreak)// 判断不通过的代码
                    ), labelBreak);
                BlockExpression block = Expression.Block(
                    new[] { loopIndex },
                    Expression.Assign(loopIndex, Expression.Constant(1)),//给 loopIndex 赋初始值 1
                    loop);
                Expression<Action> action = Expression.Lambda<Action>(block);
                var lambda = action.Compile();
                //lambda.Invoke();
            }
            {
                ParameterExpression arrayExpr = Expression.Parameter(typeof(int[]), "Array");
                ParameterExpression indexExpr = Expression.Parameter(typeof(int), "Index");
                ParameterExpression valueExpr = Expression.Parameter(typeof(int), "Value");
                Expression arrayAccessExpr = Expression.ArrayAccess(arrayExpr, indexExpr);//

                Expression<Func<int[], int, int, int>> lambdaExpr = Expression.Lambda<Func<int[], int, int, int>>(
                    Expression.Assign(arrayAccessExpr, Expression.Add(arrayAccessExpr, valueExpr)),
                    arrayExpr,
                    indexExpr,
                    valueExpr
                );

                //Console.WriteLine(arrayAccessExpr.ToString());
                //// Array[Index]

                //Console.WriteLine(lambdaExpr.ToString());
                //// (Array, Index, Value) => (Array[Index] = (Array[Index] + Value))

                //Console.WriteLine(lambdaExpr.Compile().Invoke(new int[] { 10, 20, 30 }, 0, 5));
                //// 15
            }
            {
                //NewExpression 创建一个对象
                NewExpression newDictionaryExpression = Expression.New(typeof(Dictionary<int, string>));
                //Console.WriteLine(newDictionaryExpression.ToString());
            }
            {
                //InvocationExpression 表示一个将委托或 lambda 表达式应用到一个自变量表达式列表的表达式。
                Expression<Func<int, int, bool>> largeSumTest = (num1, num2) => (num1 + num2) > 1000;

                InvocationExpression invocationExpression = Expression.Invoke(
                    largeSumTest,
                    Expression.Constant(539),
                    Expression.Constant(281));
                
                //Console.WriteLine(invocationExpression.ToString());
                //// Invoke((num1, num2) => ((num1 + num2) > 1000),539,281)
            }
            {
                // 直接返回常量值
                ConstantExpression ce1 = Expression.Constant(10);

                // 直接用我们上面创建的常量表达式来创建表达式树
                Expression<Func<int>> expr1 = Expression.Lambda<Func<int>>(ce1);
                Console.WriteLine(expr1.Compile().Invoke());
                // 10

                // --------------在方法体内创建变量，经过操作之后再返回------------------

                // 1.创建方法体表达式 2.在方法体内声明变量并附值 3. 返回该变量
                ParameterExpression param2 = Expression.Parameter(typeof(int));
                BlockExpression block2 = Expression.Block(
                    new[] { param2 },
                    Expression.AddAssign(param2, Expression.Constant(20)),
                    param2
                    );
                Expression<Func<int>> expr2 = Expression.Lambda<Func<int>>(block2);
                Console.WriteLine(expr2.Compile().Invoke());
                // 20

                // -------------------利用 GotoExpression 返回值-----------------------------

                LabelTarget returnTarget = Expression.Label(typeof(Int32));
                LabelExpression returnLabel = Expression.Label(returnTarget, Expression.Constant(10, typeof(Int32)));

                // 为输入参数+10之后返回
                ParameterExpression inParam3 = Expression.Parameter(typeof(int));
                BlockExpression block3 = Expression.Block(
                    Expression.AddAssign(inParam3, Expression.Constant(10)),
                    Expression.Return(returnTarget, inParam3),
                    returnLabel);

                Expression<Func<int, int>> expr3 = Expression.Lambda<Func<int, int>>(block3, inParam3);
                Console.WriteLine(expr3.Compile().Invoke(20));
                // 30
            }
            Console.ReadKey();
        }
    }
}
